/***********************************************
 This file is part of redia-library-app-ios.
 
 Copyright (c) 2012, 2013 Redia A/S
 
 Redia-library-app-ios is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 
 Redia-library-app-ios is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with redia-library-app-ios.  If not, see <http://www.gnu.org/licenses/>.
 
 *********************************************** */


#import "InfoGalleriXmlRpcClient201009ChannelFetchDelegate.h"
#import "InfoGalleriXmlRpcClient201009.h"
#import "XMLRPCResponse.h"
#import "XMLRPCRequest.h"
#import "XMLRPCEncoder.h"
#import "InfoObject.h"
#import "InfoMedia.h"
#import "InfoGalleriImageUrlUtils.h"
#import "defines.h"


#ifdef REDIA_APP_USE_ERROR_REPORTER
#import "errorreporter.h"
#else
#   ifndef REDIA_APP_DONT_WARN_ON_MISSING_ERROR_REPORTER
#       warning Error reporting is disabled in this build.
#   endif
#endif

#import "UrlCacheManager.h"
#import "ChannelFetchManager.h"
#import "ChannelFetchCompleteDelegate.h"
#import "RediaPublishXmlRpcClientV1.h"

@implementation InfoGalleriXmlRpcClient201009ChannelFetchDelegate

@synthesize infoObjects;
@synthesize fetchChannelsSequentially;

- (id)init {
    self = [super init];
    if (self) {
        client = [[InfoGalleriXmlRpcClient201009 alloc] init];
        pendingChannelIDs=[NSMutableArray new];
        totalChannelNum=0;
        pendingChannelCounter=0;
        cachedElements=[NSMutableArray new];
        usingNewBackend=false;
    }
    return self;
}

-(void)useNewRediaPublishClient
{
    client = [RediaPublishXmlRpcClientV1 new];
    usingNewBackend=true;
}

-(bool)isUsingNewBackend
{
    return usingNewBackend;
}

- (void)fetchGalleryForCustomer:(NSString*)customer withGallery:(NSString*)gallery delegate:(id<ChannelFetchCompleteDelegate>)delegate
{
	channelFetchCompleteDelegate = delegate;
	[self retreiveInfoObjectsForCustomer:customer withGallery:gallery];
}

-(void)fetchFromPackageForCustomer:(NSString *)customer withGallery:(NSString *)gallery delegate:(id<ChannelFetchCompleteDelegate>)delegate
{
    channelFetchCompleteDelegate = delegate;
    
    packageFetcher = [InfoGalleriPlistPackageFetcher new];
    [packageFetcher fetchPlistCacheForCustomer:customer withGallery:gallery delegate:self];
}

-(void)packageFetchComplete:(NSString *)filepath
{
    //assume filepath is the same as cache
    [self fetchFromCacheWithDelegate:channelFetchCompleteDelegate];
}
-(void)packageFetchError:(NSError *)error
{
    //failover to old channelfetcher
    [self fetchGalleryForCustomer:packageFetcher.customerId withGallery:packageFetcher.galleryId delegate:channelFetchCompleteDelegate];
}

-(void)progress:(float)newprogress
{
    //fix for progress on package:
    [channelFetchCompleteDelegate completedChannels:newprogress*100 ofTotal:100];
}

- (void)retreiveInfoObjectsForCustomer: (NSString*) customerID withGallery: (NSString*)gallery 
{
	self.infoObjects = [NSMutableArray new];
	@try {
		[client getGalleryChannels:customerID galleryId:gallery delegate:self];
	}
	@catch (NSException * e) {
		NSLog(@"received general exception during call to getGalleryChannels, using customer %@ with gallery %@: %@",customerID, gallery, e);
#ifdef REDIA_APP_USE_ERROR_REPORTER
		[ErrorReporter reportError:@"received general exception during call to getGalleryChannels" extraInformation:[NSString stringWithFormat:@"%@\nStack trace: %@",e,[e callStackSymbols]]];
#endif
        
	}
}


- (void) parseInfoObjects: (NSDictionary *) valuedict  {
	@try {
		
		NSArray* ordering = [valuedict objectForKey:@"ordering"];
        
		//DLog(@"ordering %@",ordering);
		
		NSDictionary *all_info_objects = [valuedict objectForKey:@"data"];
		
		for (NSString* elementID in ordering) {
			@try {
				NSString* infoobject_xml = [all_info_objects objectForKey:elementID];

				//DLog(@"Response object XML: %@", infoobject_xml);
				InfoObject* infoobject = [[InfoObject alloc] initWithXmlString:infoobject_xml];
                [infoobject parse];
                [infoObjects addObject:infoobject];
			}
			@catch (NSException* e) {
				NSLog(@"received exception while parsing an infoobject: %@",e);
			}
		}
        DLog(@"parsed %d infoobjects", [infoObjects count]);
		@synchronized(self) {
			pendingChannelCounter--;
			if (pendingChannelCounter<=0) {
				//all channels now fetched ok
                /*moved up to calling function
				NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
				NSString* elementCachePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"InfoGalleriElementCache.plist"];
				
				if (![valuedict writeToFile:elementCachePath atomically:YES]) {
					NSLog(@"write to cache file error: %@",elementCachePath);
				}
				*/
				[channelFetchCompleteDelegate channelFetchComplete:infoObjects];
			}
		}
	}
	@catch (NSException* e) {
		NSLog(@"received general exception while parsing some infoobjects: %@",e);
		[self generalNetworkFailure:[NSString stringWithFormat:@"received general exception while parsing some infoobjects: %@", e] errrorcode:50005 function:__PRETTY_FUNCTION__ lineno:__LINE__];

	}

}

- (void)fetchFromCacheWithDelegate:(id<ChannelFetchCompleteDelegate>)delegate
{
	self.infoObjects = [NSMutableArray new];

	channelFetchCompleteDelegate = delegate;
	
	NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
	NSString* elementCachePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"InfoGalleriElementCache.plist"];
	NSFileManager* filemgr = [[NSFileManager alloc] init];
	if ([filemgr fileExistsAtPath:elementCachePath]) {
		//old code: NSDictionary* valuedict = [NSDictionary dictionaryWithContentsOfFile:elementCachePath];
        NSArray* valuedictarray = [NSArray arrayWithContentsOfFile:elementCachePath];
		if (valuedictarray != nil && [valuedictarray count]>0) {
			ALog(@"Going into off-line mode");
			[[UrlCacheManager instance] setIsOffLineMode:TRUE];
            pendingChannelCounter = [valuedictarray count];
            int cur_idx=0;
            for (NSDictionary* valuedict in valuedictarray) {
                [self parseInfoObjects:valuedict];
                if ([channelFetchCompleteDelegate respondsToSelector:@selector(completedChannels:ofTotal:)]) {
                    [channelFetchCompleteDelegate completedChannels:++cur_idx ofTotal:totalChannelNum+1]; //fixme: has no effect since thread never returns to gui thread before we are finished
                }

            }
		} else {
			[self generalNetworkFailure:[NSString stringWithFormat:@"couldn't read cache file, file exists but is unreadable by NSArray"] errrorcode:60001 function:__PRETTY_FUNCTION__ lineno:__LINE__];
		}
	} else {
		NSString* msg = [NSString stringWithFormat:@"Beklager, der er opstået en fejl mens netværket blev kontaktet. Prøv venligst igen senere. [60002]"];
		UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Problemer med netværket" 
														message:msg
													   delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
		[alert show];	
	}
}

- (void)fetchSingleInfoObjectForCustomer:(NSString*)customer objectId:(NSString*)objectId delegate:(id<InfoObjectFetchCompleteDelegate>)delegate
{
    self.infoObjectFetchCompleteDelegate = delegate;
    [client getInfoObject:customer infoObjectId:objectId delegate:self];
}


- (void)request: (XMLRPCRequest *)request didReceiveResponse: (XMLRPCResponse *)response
{
	DLog(@"Response for request method: %@", [request method]);
	if ([response isFault]) {
        ALog(@"Fault code: %@", [response faultCode]);
		ALog(@"Fault string: %@", [response faultString]);
        
        if ([request.method isEqualToString:@"getInfoObject"]) {
            // Notify the delegates that the fetch failed.
            NSString *infoObjectId;
            if ([request.parameters count] > 1) {
                if ([[request.parameters objectAtIndex:1] isKindOfClass:[NSString class]]) {
                    infoObjectId = [request.parameters objectAtIndex:1];
                }
            }
            
            if (infoObjectId &&
                self.infoObjectFetchCompleteDelegate &&
                [self.infoObjectFetchCompleteDelegate respondsToSelector:@selector(infoObjectFetchComplete:)]) {
                [self.infoObjectFetchCompleteDelegate infoObjectFetchFailed:infoObjectId];
            }
        }
        else {
            [self generalNetworkFailure:[NSString stringWithFormat:@"received unknown response to XMLRPCRequest, Fault string: %@",[response faultString]] errrorcode:[[response faultCode] intValue] function:__PRETTY_FUNCTION__ lineno:__LINE__];
        }

    } else {
		//DLog(@"Response object XML: %@", [response body]);
        //DLog(@"Parsed response: %@", [response object]);
        //DLog(@"Parsed response's class: %@", [[[response object] lastObject] class] );
		
		if ([[request method] isEqualToString:@"getGalleryChannels"]) {
			
			if (response != nil) {
				if ([response object] != nil) {
					NSMutableArray *valuearray = [response object];
					if (valuearray != nil && [valuearray isKindOfClass:[NSMutableArray class]]) {
						@synchronized(self) {
							for (id val in valuearray) {
								if ([val isKindOfClass:[NSDictionary class]]) {
									NSDictionary* dict = val;
									NSString* channelId = [dict objectForKey:@"channelId"];
									NSString* customer = [dict objectForKey:@"customerId"];
									ALog(@"Response channelId: %@", channelId);
									//DLog(@"Fetching objects...");
									pendingChannelCounter++;
                                    if (!fetchChannelsSequentially || pendingChannelCounter==1) {
                                        [client getChannelInfoObjects:customer channelId:channelId delegate:self];
                                        totalChannelNum=pendingChannelCounter;
                                    } else {
                                        [pendingChannelIDs addObject:[NSArray arrayWithObjects:customer,channelId, nil]];
                                        totalChannelNum=[pendingChannelIDs count];
                                    }
								}
							}
						}
						
					} else {
						[self generalNetworkFailure:[NSString stringWithFormat:@"received unknown response to getGalleryChannels, request: %@\nresponse: %@",[request body],[response object]] errrorcode:50002 function:__PRETTY_FUNCTION__ lineno:__LINE__];
					}
					
				}
			}
			//DLog(@"Parsed response: %@", [response object]);
		} else if ([[request method] isEqualToString:@"getChannelInfoObjects"]) {
			//DLog(@"Parsed response's class: %@", [[response object] class] );
			//DLog(@"Parsed response: %@", [response object]);
            
            if (fetchChannelsSequentially && [pendingChannelIDs count]>0) {
                ALog(@"Fetching objects for next channel...");
                NSArray* temparray = [pendingChannelIDs objectAtIndex:0];
                [pendingChannelIDs removeObjectAtIndex:0];
                NSString* customer2 = [temparray objectAtIndex:0];
                NSString* channel2 = [temparray objectAtIndex:1];
                [client getChannelInfoObjects:customer2 channelId:channel2 delegate:self];
            }
            
            if ([channelFetchCompleteDelegate respondsToSelector:@selector(completedChannels:ofTotal:)]) {
                [channelFetchCompleteDelegate completedChannels:totalChannelNum-pendingChannelCounter ofTotal:totalChannelNum+1];
            }
            
			NSDictionary *valuedict = [response object];
			
			if (valuedict != nil && [valuedict isKindOfClass:[NSDictionary class]]) {
                [cachedElements addObject:valuedict];
								
				[self parseInfoObjects: valuedict];
                @synchronized(self) {
                    if (pendingChannelCounter<=0) {
                        DLog(@"writing elements to cache file");
                        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
                        NSString* elementCachePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"InfoGalleriElementCache.plist"];
                        
                        if (![cachedElements writeToFile:elementCachePath atomically:YES]) {
                            NSLog(@"write to cache file error: %@",elementCachePath);
                        }
                        [cachedElements removeAllObjects];
                    }
                }

			} else {
				[self generalNetworkFailure:[NSString stringWithFormat:@"received unknown response to getChannelInfoObjects, request: %@\nresponse: %@",[request body],[response object]] errrorcode:50004 function:__PRETTY_FUNCTION__ lineno:__LINE__];
			}
			
		}
        else if ([request.method isEqualToString:@"getInfoObject"]) {
            NSDictionary *valueDict = [response object];
            if (valueDict && [valueDict isKindOfClass:[NSDictionary class]]) {
                NSString *infoObjectXML = [valueDict objectForKey:@"data"];
                if (infoObjectXML) {
                    InfoObject *infoObject = [[InfoObject alloc] initWithXmlString:infoObjectXML];
                    // Try to hand the info object over to the delegate.
                    @try {
                        if (self.infoObjectFetchCompleteDelegate && [self.infoObjectFetchCompleteDelegate respondsToSelector:@selector(infoObjectFetchComplete:)]) {
                            [infoObject parse];
                            [self.infoObjectFetchCompleteDelegate infoObjectFetchComplete:infoObject];
                        }
                    }
                    @catch (NSException *exception) {
                        // Silently ignore errors.
                        ALog(@"ERROR: unable to parse info object.");
                    }
                }
                else {
                    ALog(@"Error parsing info object from XML description.");
                }
            }
            else {
                ALog(@"Unexpected return type in XMLRPC request.");
            }
        }
        
        else {
			DLog(@"Unknown request type");
			DLog(@"Parsed response: %@", [response object]);
		}

    }
	
    //DLog(@"Response body: %@", [response body]);
}

- (void)generalNetworkFailure:(NSString*)description errrorcode:(int)code function:(const char*)func_name lineno:(int)line
{
#ifdef REDIA_APP_USE_ERROR_REPORTER
	[ErrorReporter reportError:[NSString stringWithFormat:@"[%s:%d] [code %d]",func_name,line,code] extraInformation:description];
#endif
	NSString* msg = [NSString stringWithFormat:@"Beklager, der er opstået en fejl mens netværket blev kontaktet. Prøv venligst igen senere. [%d]",code ];
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Problemer med netværket" 
													message:msg
												   delegate:self cancelButtonTitle:@"OK" otherButtonTitles: nil];
	[alert show];	
}

- (void)request: (XMLRPCRequest *)request didFailWithError: (NSError *)error
{
	ALog(@"XMLRPCRequest %@ didFailWithError: %@", [request method], error);
	//[self generalNetworkFailure:[NSString stringWithFormat:@"XMLRPCRequest didFailWithError: %@", error] errrorcode:[error code] function:__PRETTY_FUNCTION__ lineno:__LINE__];
    if ([[request method] isEqualToString:@"getGalleryChannels"]) {
        ALog(@"no gallery arrived - trying cached elements instead");
        [self fetchFromCacheWithDelegate:channelFetchCompleteDelegate];
        
    }
    else if ([[request method] isEqualToString:@"getChannelInfoObjects"]) {
        if (error!=nil && [error code]==kCFURLErrorTimedOut) {
            /*
            ALog(@"Retrying request with method %@, params %@",[request method], [request parameters]);
            [client retryRequest:request delegate:self];
             */
            ALog(@"ERROR: getChannelInfoObjects TIMED OUT");
        }
        [self generalNetworkFailure:@"getChannelInfoObjects timed out" errrorcode:50010 function:__PRETTY_FUNCTION__ lineno:__LINE__];
    }
    else if ([[request method] isEqualToString:@"getInfoObject"]) {
        if (error && error.code == kCFURLErrorTimedOut) {
            ALog(@"ERROR: getInfoObject TIMED OUT");
        }
        
        // Notify the delegates that the fetch failed.
        NSString *infoObjectId;
        if ([request.parameters count] > 1) {
            if ([[request.parameters objectAtIndex:1] isKindOfClass:[NSString class]]) {
                infoObjectId = [request.parameters objectAtIndex:1];
            }
        }
        
        if (infoObjectId &&
            self.infoObjectFetchCompleteDelegate &&
            [self.infoObjectFetchCompleteDelegate respondsToSelector:@selector(infoObjectFetchComplete:)]) {
            [self.infoObjectFetchCompleteDelegate infoObjectFetchFailed:infoObjectId];
        }
    }
    else {
        ALog(@"WARNING: unhandled method");
    }
}

- (void)request: (XMLRPCRequest *)request didReceiveAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge
{
	ALog(@"didReceiveAuthenticationChallenge");
	[self generalNetworkFailure:@"XMLRPCRequest didReceiveAuthenticationChallenge" errrorcode:50000 function:__PRETTY_FUNCTION__ lineno:__LINE__];
}

- (void)request: (XMLRPCRequest *)request didCancelAuthenticationChallenge: (NSURLAuthenticationChallenge *)challenge
{
	ALog(@"didCancelAuthenticationChallenge");
	[self generalNetworkFailure:@"XMLRPCRequest didCancelAuthenticationChallenge" errrorcode:50001 function:__PRETTY_FUNCTION__ lineno:__LINE__];
}

-(BOOL)request:(XMLRPCRequest *)request canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
{
    return NO;
}


#pragma mark -
#pragma mark - UIAlertViewDelegate

- (void)alertView:(UIAlertView *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
{
	// use "buttonIndex" to decide your action

	//NB: app store apps must never terminate the app!
#ifdef INFOGALLERI_XMLRPC_CLIENT_EXIT_ON_FAIL
	exit(0);
#endif
}

- (InfoGalleriXmlRpcClient201009*)getClient {
	return client;
}

@end
